﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;

namespace Brain
{
    public class BCFFileData
    {
        /// <summary>
        /// Returns true if the Data property loaded
        /// is compressed, false if not.
        /// </summary>
        public bool IsCompressed { get; protected set; }

        public byte[] Data;

        public BCFFileData()
        {
        }
        public BCFFileData(FileInfo from)
        {
            IsCompressed = false;

            Data = new byte[from.Length];
            using (FileStream stream = from.OpenRead())
            {
                using (BinaryReader reader = new BinaryReader(stream))
                {
                    reader.Read(Data, 0, Data.Length);
                }
            }
        }

        public void Decompress(Stream stream)
        {
        }

        public void CompressTo(Stream stream)
        {
            if (IsCompressed)
            {
                Console.WriteLine("File already compressed");
#if DEBUG
                Debug.WriteLine("File already compressed");
#endif
                return;
            }

            List<BCFRangeData> range = new List<BCFRangeData>();
            List<byte> data = new List<byte>();

            long firstView = 0;
            byte lastByte = Data[0];
            ushort equal = 0;
            long remove = 0;
#if WINDOWS
            for (long i = 1; i < Data.LongLength; i++)
#else
            for (long i = 1; i < Data.Length; i++)
#endif
            {
                byte b = Data[i];
                if (lastByte == b)
                {
                    if (equal == 0)
                    {
                        firstView = i;
                    }
                    equal++;
                }
                else
                {
                    data.Add(b);
                    //BCF File
                    //8 bits - byte - Data
                    //16 bits - ushort - Repetition
                    //64 bits - ulong - Position
                    //= 88 bits

                    // Value must be at least greater than 24 to compression
                    // work
                    if (equal > 88)
                    {
                        BCFRangeData rangeData = new BCFRangeData(lastByte, equal, firstView - remove);
                        remove += equal;
                        range.Add(rangeData);
                    }
                    firstView = 0;
                    equal = 0;
                }
                lastByte = b;
            }

            if (range.Count == 0)
            {
                // Nothing to compress 
                tempData = new List<byte>();
                IsCompressed = false;
                Write(false);
                tempData.AddRange(Data);
                Data = tempData.ToArray();

                return;
            }

            tempData = new List<byte>();

            

            for (int i = 0; i < range.Count; i++)
            {
                BCFRangeData r = range[i];

                Write(r.Data);
                Write(r.Repetition);
                Write(r.StartIndex);
            }

            List<byte> end = new List<byte>();
            end.AddRange(tempData);
            end.AddRange(data);

            long difference = Data.Length - end.Count;


            Data = end.ToArray();
            this.IsCompressed = true;


#if DEBUG
            Debug.WriteLine("File compressed "  + difference + "bytes");
#endif
        }

        private List<byte> tempData;
        private void Write(byte b)
        {
            tempData.Add(b);
        }
        private void Write(int b)
        {
            byte[] data = BitConverter.GetBytes(b);
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(data);
            }
            Write(data);
        }
        private void Write(long b)
        {
            byte[] data = BitConverter.GetBytes(b);
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(data);
            }
            Write(data);
        }
        private void Write(bool b)
        {
            byte[] data = BitConverter.GetBytes(b);
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(data);
            }
            Write(data);
        }
        private void Write(byte[] b)
        {
            tempData.AddRange(b);
        }
    }
}
